--[[
   spell class
]]--

Optimizer.Spell = { _instance = nil };
Optimizer.Spell.__index = Optimizer.Spell;


function Optimizer.Spell:new()
    local new_inst = {
        name = nil,
        powercost = nil,
        cooldown = nil,
        Events = {
            --everything went well, damage applied to target
            ["SPELL_DAMAGE"] = function(om) om:Cast() end,
            
            --cast failed, no target, power etc
            ["SPELL_CAST_FAILED"] = function(om) om:spellFail() end,
            
            --Spell missed but cost Power
            ["SPELL_MISSED"] = function(om) om:spellMiss() end,
            
            --spell interrupt by kick etc
            ["SPELL_INTERRUPT"] = function(om) om:spellInterrupt() end,
            
            --start of casting, instant/channels dont trigger
            ["SPELL_CAST_START"] = function(om) om:spellCastStart() end,
            
            --triggered by channels and instants
            ["SPELL_CAST_SUCCESS"] = function(om) om:spellCastSuccess() end,
            
            --update method to check for cd etc, writes to log
            ["UPDATE"] = function(om) om:Update() end
        },
        
        noPower = false,
        noTarget = false,
        noRange = false,
        noSight = false,
        
        min = 0,
        max = 0,
        inCD = false,
        inCD2 = false,
        next = 0,
        
        bookID = nil
        
    };    
    
    self._instance = setmetatable(new_inst,Optimizer.Spell);
    return self._instance;
end

function Optimizer.Spell:GetInstance()
    return self._instance;
end

function Optimizer.Spell:init()
    self.bookID = Optimizer:GetBookId(self.name);
end

function Optimizer.Spell:onCombat(time)
    self.next = time; 
    table.insert(Optimizer._UpdateShedule,
    {
        [self.next] = self.name;
    }
    );
end



function Optimizer.Spell:exists()
    local name, rank, icon, cost, isFunnel, powerType, castTime, minRange, maxRange = GetSpellInfo(self.name);
    if name ~= nil then
        return true;
    else
        return false;
    end
end

function Optimizer.Spell:GetSpellCost()
    local _, _, _, cost, _, _, _, _, _ = GetSpellInfo(self.name);
    return cost;
end

function Optimizer.Spell:Cast()
    self.noPower = false;
    self.noTarget = false;
    self.noRange = false;
    self.noSight = false;
    
    local _, _, lag = GetNetStats();
    local time = GetTime();
    local timeOff = time-self.next-(lag/1000);    
    local secOff = math.floor(timeOff);
    local msecOff = math.floor((timeOff-math.floor(timeOff))*1000);

    if secOff < 0 then
        secOff = 0;
    end
    if msecOff < 0 then
        msecOff = 0;
    end
    
    local eff = secOff + (msecOff/1000);
    local eff = ((eff*100)/self.cooldown)-100;
    if eff > 0 then
        eff = 0;
    end
--~     DEFAULT_CHAT_FRAME:AddMessage(self.name.." off: "..secOff.." Sec. "..msecOff.." mSec."); 
--~     DEFAULT_CHAT_FRAME:AddMessage(self.name..": "..eff.."% efficiency"); 

    Optimizer.myLog:AddEntry("eff",time,self.name,math.abs(eff),'');
    Optimizer.myLog:AddEff(self.name,math.abs(eff));
    
    self.next = time + self.cooldown;
    
    table.insert(Optimizer._UpdateShedule,
    {
        [self.next] = self.name;
    }
    );
    table.sort(Optimizer._UpdateShedule,Optimizer.CompShedule);
    -- update the shedule time if needed
    Optimizer.nextSheduleTime,Optimizer.nextSheduleSpell = next(Optimizer._UpdateShedule[#Optimizer._UpdateShedule]);
    
    self.inCD2 = true;
    
--~     if timeOff > self.max then
--~         self.max = timeOff;
--~     end
--~     if timeOff < self.min then
--~         self.min = timeOff;
--~     end
--~     if self.min == 0 then
--~         self.min = timeOff;
--~         self.max = timeOff;
--~     end
end

function Optimizer.Spell:makeLog()
    
end

function Optimizer.Spell:Update(...)
   local start, duration, enabled = GetSpellCooldown(self.bookID, BOOKTYPE_SPELL);
   local time = GetTime();
   local sec = math.floor(time);
   local logged = false;
--~    DEFAULT_CHAT_FRAME:AddMessage(self.name.." update");
    if enabled == 0 then
        --spell active
    elseif ( start > 0 and duration > 0) then
        if self.inCD == false then
            self.inCD = true;
        end
    else
        if self.inCD == true then
                Optimizer.myLog:AddEntry("info",time,self.name,'',self:State2Str());
            logged = true;
            self.inCD = false;
        end
    end
    if time >= self.next then
        if self.inCD2 == true then
            if logged == false then
                Optimizer.myLog:AddEntry("info",time,self.name,'',self:State2Str());
            end
            self.inCD2 = false;
        end
    end    
end

function Optimizer.Spell:spellMiss()

end

function Optimizer.Spell:spellInterrupt()

end

function Optimizer.Spell:spellCastStart(...)
    self:Cast(...);
end

function Optimizer.Spell:spellCastSuccess()

end

function Optimizer.Spell:spellFail(...)

    local sec = math.floor(arg1);
    local msec = (arg1-math.floor(arg1))*1000;
    
    if arg12 == SPELL_FAILED_UNIT_NOT_INFRONT then
        Optimizer._playerInSight = false;
        self:spellFail_noSight(sec,msec,...);
    end

    if arg12 == SPELL_FAILED_OUT_OF_RANGE then
        Optimizer._playerInRange = false;
        self:spellFail_noRange(time,...);                      
    end

    if 
        arg12 == ERR_OUT_OF_RAGE or
        arg12 == ERR_OUT_OF_FOCUS or
        arg12 == ERR_OUT_OF_HEALTH or
        arg12 == ERR_OUT_OF_MANA or
--~         arg12 == ERR_OUT_OF_RANGE or
        arg12 == ERR_OUT_OF_RUNES or
        arg12 == ERR_OUT_OF_RUNIC_POWER 
    then
        self:spellFail_noPower(sec,msec,...); 
    end  
end

function Optimizer.Spell:spellFail_noSight()
    self.noSight = true;
end

function Optimizer.Spell:spellFail_noRange()
    self.noRange = true;
end

function Optimizer.Spell:spellFail_noPower()
    self.noPower = true;
end

function Optimizer.Spell:spellFail_noTarget()
    self.noTarget = true;
end

function Optimizer.Spell:State2Str()
    local str = "";
    if self.noSight == true then
        str = str.."noSight";
    end
    if self.noRange == true then
        str = str.."noRange";
    end
    if self.noPower == true then
        str = str.."noPower";
    end
    if self.noTarget == true then
        str = str.."noTarget";
    end
    if str == "" then
        str = "unknown";
    end
    
    return str;
end
